<?php

namespace App\Library;

use Carbon\Carbon;
use Illuminate\Support\Str;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Contracts\Filesystem\Filesystem as DiskFileSystem;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class FileSystem
{
    private string $directory = '/';
    private string $diskName;
    private DiskFileSystem $disk;

    public static function init(string $diskName = 'public'): static
    {
        $fileSystem = new static;
        $fileSystem->diskName = $diskName;
        return $fileSystem;
    }

    /**
     * 获取列表
     * @param int $offset 起始页
     * @param int $length 结束页
     * @param string|null $search 搜索项
     * @return Collection 返回集合
     */
    public function lists(int $offset = 0, int $length = 100, string $search = null): Collection
    {
        $data = $this->directories()->merge($this->files());
        if ($search !== null && $search !== '') {
            $data = $data->filter(function ($value) use ($search) {
                return Str::contains($value['name'], $search);
            });
        }
        $total = $data->count();
        $data = $data->slice($offset, $length);
        if ($this->getDirectory() !== '/') {
            $name = array_filter(explode('/', $this->getDirectory()));
            array_unshift($name, '/');
            $data->prepend([
                'type' => 'path',
                'size' => '',
                'name' => $name,
                'lastModified' => '',
                'pathInfo' => [
                    'extension' => 'path'
                ]
            ]);
        }
        return collect([
            'total' => $total,
            'data' => $data->values()->toArray()
        ]);
    }

    /**
     * 文件列表
     * @return Collection
     */
    public function files(): Collection
    {
        $disk = $this->getDisk();
        $fileArray = $disk->files($this->getDirectory());
        $files = [];
        foreach ($fileArray as $file) {
            $files[] = [
                'type' => 'file',
                'size' => $disk->size($file),
                'name' => $file,
                'url' => asset('storage/' . $file),
                'lastModified' => Carbon::parse($disk->lastModified($file))->toISOString(),
                'pathInfo' => pathinfo($file)
            ];
        }
        $files = collect($files);
        return $files->reject(function ($value) {
            return $value['name'] === '.gitignore';
        })->values();
    }

    /**
     * 文件夹列表
     * @return Collection
     */
    public function directories(): Collection
    {
        $directoryArray = $this->getDisk()->directories($this->getDirectory());
        $directories = [];
        foreach ($directoryArray as $directory) {
            $directory = explode('/', $directory);
            $directories[] = [
                'type' => 'directory',
                'size' => '',
                'name' => end($directory) . '/',
                'lastModified' => '',
                'pathInfo' => [
                    'extension' => 'folder'
                ]
            ];
        }
        return collect($directories);
    }

    /**
     * 创建文件
     * @param string $directory
     * @return bool
     */
    public function makeDirectory(string $directory): bool
    {
        return $this->getDisk()->makeDirectory($directory);
    }

    /**
     * 删除文件
     * @param string $directory
     * @return bool
     */
    public function deleteDirectory(string $directory): bool
    {
        return $this->getDisk()->deleteDirectory($directory);
    }

    /**
     * 下载文件(流)
     * @param string $file
     * @return StreamedResponse
     */
    public function download(string $file): StreamedResponse
    {
        if ($this->getDisk()->exists($file)) {
            return $this->getDisk()->download($file);
        }

        throw new NotFoundHttpException;
    }

    /**\
     * 删除文件
     * @param $paths
     * @return bool
     */
    public function delete($paths): bool
    {
        return $this->getDisk()->delete($paths);
    }

    /**
     * @return string
     */
    public function getDirectory(): string
    {
        return $this->directory;
    }

    /**
     * @param string $directory
     * @return $this
     */
    public function setDirectory(string $directory): static
    {
        $this->directory = $directory;
        return $this;
    }

    /**
     * @return DiskFileSystem
     */
    public function getDisk(): DiskFileSystem
    {
        if (empty($this->disk)) {
            $this->setDisk($this->diskName);
        }
        return $this->disk;
    }

    /**
     * @param string|null $diskName
     * @return $this
     */
    public function setDisk(?string $diskName): static
    {
        $this->disk = Storage::disk($diskName ?: $this->diskName);
        return $this;
    }

    /**
     * @return string
     */
    public function getDiskName(): string
    {
        return $this->diskName;
    }

    /**
     * @param string $diskName
     * @return $this
     */
    public function setDiskName(string $diskName): static
    {
        $this->diskName = $diskName;
        return $this;
    }
}
